/*
 * Description: 资源泄露相关事件上报
 */
#ifndef LOG_RESOURCE_EVENT_HANDLER_H
#define LOG_RESOURCE_EVENT_HANDLER_H

#include <iostream>
#include <windows.h>
#include <psapi.h>
#include <list>
#include <map>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <chrono>
#include <vector>
#include <sstream>
#include <winver.h>

namespace DFX {
using namespace std;
using KPRIORITY = LONG;

using ResourceType = enum TagResourceType {
    RESOUCE_TYPE_UNKNOWN,
    RESOUCE_TYPE_HANDLE,
    RESOUCE_TYPE_PROCESS,
    RESOUCE_TYPE_MEMORY,
    RESOUCE_TYPE_THREAD
};


using UnicodeString = struct TagUnicodeString {
    USHORT length;
    USHORT maxLength;
    PWCH buffer;
};

using SystemProcess = struct TagSystemProcess {
    ULONG nextEntryOffset;
    ULONG numberOfThread;
    LONGLONG privateWorkingSetSize;
    DWORD spareCommon[4]; // 保留4字段，不需要使用
    LARGE_INTEGER createTime;
    LARGE_INTEGER userTime;
    LARGE_INTEGER kernelTime;
    UnicodeString processName;
    KPRIORITY basePriority;
    HANDLE processId;
    HANDLE parentProcessId;
    ULONG handleCount;
    ULONG sessionId;
    ULONG_PTR pageDirectoryBase;

    // memeroy infor
    SIZE_T peakVirutalSize;
    SIZE_T virtualSize;
    ULONG pageFaultCount;
    SIZE_T peakWorkingSetSize;
    SIZE_T workingSetSize;
    SIZE_T spareMemory[7]; // 保留的7个字段，不需要使用

    // io info
    LARGE_INTEGER spareIo[6]; // 保留的6个字段，不需要使用
};

class ResourceInfo {
public:
    HANDLE processId = NULL;
    std::wstring proName = L"";
    std::wstring proVersion = L"";
    std::list<int64_t> resouceValuleHistory = {};
    int64_t resouceValule = 0;
    ResourceType resouceType = RESOUCE_TYPE_UNKNOWN;
    bool hasReport = false;
    // 对于3周期没有故障的，需要从列表删除
    bool lastErrorFlag = false;

    ResourceInfo(){};
    ResourceInfo(HANDLE id, const std::wstring &name, ResourceType type, int64_t valule)
    {
        proName = name;
        processId = id;
        resouceType = type;
        resouceValule = valule;
        resouceValuleHistory.push_back(valule);
    }
    ~ResourceInfo(){};
};


class ResourceEventHandler {
public:
    void ReportResEvents();
    bool Init();
    void DeInit();
    ~ResourceEventHandler();
private:
    const DWORD BUFFER_LEN_1K = 1024;
    const DWORD BUFFER_LEN_1M = BUFFER_LEN_1K * BUFFER_LEN_1K;
    PVOID sysInfoBuffer = nullptr;
    HANDLE runEvent = nullptr;
    HMODULE ntDllModule = nullptr;

    ULONG sysInfoBufferLen = BUFFER_LEN_1M;
    const std::wstring NTDLL_NAME = L"Ntdll.dll";
    const std::string QUERY_SYSTEM_FUN_NAME = "NtQuerySystemInformation";
    const DWORD MAX_PRO_NAME_LEN = 100;
    const DWORD MAX_VERSION_LEN = 30;
    const int VERSION_MAJOR_SHIFT = 16;
    const DWORD MAX_SYS_INFORMATION_IN_MB = 10;
    const DWORD MAX_SYS_INFORMATION_IN = MAX_SYS_INFORMATION_IN_MB * BUFFER_LEN_1M;

    const UINT64 handleLeakThreshold = 10000;
    const UINT64 memoryLeakThreshold = 2048;
    const UINT64 threadLeakThreshold = 500;
    const UINT64 processLeakThreshold = 200;
    const UINT64 leakTimesThreshold = 3;
    const ULONG STATUS_INFO_LENGTH_MISMATCH = 0xC0000004L;
    std::map<HANDLE, ResourceInfo> handleErrorResource = {};
    std::map<HANDLE, ResourceInfo> memErrorResource = {};
    std::map<HANDLE, ResourceInfo> threadErrorResource = {};
    std::map<std::wstring, ResourceInfo> processFaultResource = {}; // 进程数量异常

    const vector<string> RESOURCE_EVENT_NAME = { "", "SA_VM_HANDLE_LEAK", "SA_VM_PROCESS_LEAK", "SA_VM_MEMORY_LEAK",
        "SA_VM_THREAD_LEAK" };
    using NtQueryInformationClass = enum TagNtQueryInformationClass { SYSTEM_PROCESS_INFORMATION = 5 };
    using FunNtQuerySystemInformation = LONG (*)(NtQueryInformationClass, PVOID, ULONG, PULONG);
    FunNtQuerySystemInformation NtQuerySystemInformation = nullptr;

    void CreateSysInforBuffer(ULONG bufferSize);
    bool QuerySystemProcessInfomation();
    void GetResource(std::list<ResourceInfo> &faultInfo);
    wstring GetFileVersion(const wstring &filePath);
    wstring GetProcVersionInfo(HANDLE processId);
    int RemoveRepairResouce();
    void GetResouceLeakData(vector<map<string, string>> &eventDatas, ResourceInfo &info);
    vector<map<string, string>> GetAllLeakEvents();
    void CheckResouceImpl(ResourceInfo srcInfo, std::map<HANDLE, ResourceInfo> &targetInfo);
    void CheckResouce(std::list<ResourceInfo> &faultInfo);
};
}

#define RES_EVENT_HANDLER SingletonNew<DFX::ResourceEventHandler>::GetInstance()

#endif